package com.yn.xgame;

import com.yn.xgame.message.IFuncMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * ThreadServer
 * Created by wangboo on 2017/7/11.
 */
public class ThreadServer implements IServer, Runnable {

    private static final Logger logger = LoggerFactory.getLogger(ThreadServer.class);
    private static final int STATE_MASK_READY = 0b0;
    private static final int STATE_MASK_RUNNING = 0b1;
    private static final int STATE_MASK_TRY_STOP = 0b10;
    private static final int STATE_MASK_STOPPED = 0b100;

    private Thread thread;
    private LinkedBlockingQueue<IMessage> messageQueue;
    private int state = 0;
    private ITimerManager timerManager;


    public ThreadServer(String serverName) {
        this(serverName, 1024);
    }

    public ThreadServer(String serverName, int maxQueueSize) {
        if (serverName == null || serverName.isEmpty()) throw new IllegalArgumentException("serverName cannot be blank nor empty");
        thread = new Thread(this);
        thread.setName(serverName);
        messageQueue = new LinkedBlockingQueue<>(maxQueueSize);
    }

    public void useTimerManager() {
        timerManager = new WheelLikeTimerManager();
    }

    @Override
    public void startup() {
        if (state != STATE_MASK_READY) return;
        state = STATE_MASK_RUNNING;
        thread.start();
    }

    @Override
    public void shutdown() {
        if ((state & STATE_MASK_RUNNING) != 0)  return;
        state |= STATE_MASK_TRY_STOP;
    }

    @Override
    public void pushMessage(IMessage message) {
        messageQueue.add(message);
    }

    @Override
    public boolean handleMessage(IMessage message) {
        if (message instanceof IFuncMessage) {
            ((IFuncMessage) message).apply();
            return true;
        }
        return false;
    }

    @Override
    public ITimerManager getTimerManager() {
        return timerManager;
    }

    @Override
    public void run() {
        while(state == STATE_MASK_RUNNING) {
            try {
                IMessage msg = messageQueue.poll(50, TimeUnit.MILLISECONDS);
                if (msg != null) handleMessage(msg);
            } catch (Throwable e) {
                logger.error(Thread.currentThread().getName() + " error happened: ", e);
            } finally {
                if (timerManager != null) {
                    timerManager.clockTick(System.currentTimeMillis());
                }
            }
        }
        logger.warn("{} is shutting down, left {} message remaining.", Thread.currentThread().getName(), messageQueue.size());
        int maxQueue = 10_0000;
        for (int i=0;i<maxQueue;i++) {
            try {
                IMessage msg = messageQueue.poll();
                if (msg != null) {
                    handleMessage(msg);
                }
            } catch (Throwable e) {
                logger.error(Thread.currentThread().getName() + " error happened: ", e);
            }
        }
        state = STATE_MASK_STOPPED;
        logger.warn("{} is stopped", Thread.currentThread().getName());
    }

    public void join() throws InterruptedException {
        thread.join();
    }

}
